package com.ls.fw.data.search.impl.lucene.handler;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.ls.fw.data.annotation.IndexField;
import com.ls.fw.data.annotation.IndexTable;
import com.ls.fw.data.bean.Column;
import com.ls.fw.data.bean.Table;
import com.ls.fw.data.enums.FieldType;
import com.ls.fw.data.utils.NameUtil;
import com.ls.fw.data.utils.TypeUtil;

public class BeanHandler {

	private static final Map<Class<?>, List<Column>> infocache = new ConcurrentHashMap<Class<?>, List<Column>>();

	public static final String GET = "get";
	public static final String SET = "set";
	public static final String IS = "is";

	// 是否将日期转化为数字
	private boolean dateToLong = false;
	//索引名称是否加盐值
	private boolean indexNameHasSalt = false;
	
 
	
	public void setDateToLong(boolean dateToLong) {
		this.dateToLong = dateToLong;
	}

	public boolean isDateToLong() {
		return dateToLong;
	}

	public boolean isIndexNameHasSalt() {
		return indexNameHasSalt;
	}

	public void setIndexNameHasSalt(boolean indexNameHasSalt) {
		this.indexNameHasSalt = indexNameHasSalt;
	}
	
	/**
	 * 获取实体类型的属性(无属性值)
	 * @author 李昇
	 * 2014年4月19日 下午1:51:49
	 * @param obj
	 * @param excludeType
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public List<Column> toColumns(Class<?> objClass, final Class<?>... excludeType)
			throws SecurityException, NoSuchMethodException, Exception {
		List<Column> list = infocache.get(objClass);
		if (list == null) {
			String tableName = NameUtil.firstCharToLower(objClass.getSimpleName());
			boolean hasAnntation = objClass.isAnnotationPresent(IndexTable.class);
			if(hasAnntation){
				list = this.toColumnsAnnotation(objClass, excludeType);
			}else{
				list = new ArrayList<Column>();
				Field[] fields = objClass.getDeclaredFields();
				boolean include = false;
				Table table = new Table("",tableName);
				table.setAlias(tableName);
				for (Field field : fields) {
					include = this.isInclude(field.getType(), excludeType);
					if (!include) {
						try {
							Column c = new Column(field.getName());
							c.setTable(table);
//							c.setFieldType(FieldType.)
//							ColumnProptery cp = new ColumnProptery();
//							cp.setHostType(objClass);
//							cp.setType(field.getType());
//							cp.setName(field.getName());
//							cp.setField(field);
							c.setAlias(field.getName());
							if (!this.isCollection(field)) {
								if (!this.isJavaClass(field.getType())) { 
									c.setForeignkey(true);
									field = field.getType().newInstance().getClass().getDeclaredField(IndexField.DEFAULT_fk);
									c.setForeignkeyField(new Column(IndexField.DEFAULT_fk));
								}
							}
						    c.setFieldType(this.getFieldType(field.getType()));
							table.addField(c);
							c.check(this.indexNameHasSalt);
							list.add(c);
						} catch (Exception e) {
							e.printStackTrace();
						}
					}  
				}
			}
			synchronized (infocache) {
				if(list!=null){
					infocache.put(objClass, list);
				}
			}
		} 
		return list;
	}
	
	private FieldType getFieldType(Class<?> fieldType){
		FieldType t = null;
		Class<?> type = fieldType;
		if (type.isPrimitive()) {// 判断是否为基本类型
			if (double.class.equals(type)) {
				 t = FieldType.doublex;
			} else if (float.class.equals(type)) {
				t = FieldType.floatx;
			}  else if (long.class.equals(type)) {
				t = FieldType.longx;
			} else if (int.class.equals(type)) {
				t = FieldType.integer;
			} else if (short.class.equals(type)) {
				 t = FieldType.integer;
			} else if (byte.class.equals(type)) {
				 t = FieldType.integer;
			} else if (boolean.class.equals(type)) {
				t = FieldType.booleanx;
			} else if (char.class.equals(type)) {
				t = FieldType.string;
			}
		} else if (Double.class.equals(type)) {
			t = FieldType.doublex;
		} else if (Float.class.equals(type)) {
			t = FieldType.floatx;
		} else if (Long.class.equals(type)) {
			t = FieldType.longx;
		} else if (Integer.class.equals(type)) {
			 t = FieldType.integer;
		} else if (Short.class.equals(type)) {
			 t = FieldType.integer;
		} else if (Byte.class.equals(type)) {
			 t = FieldType.integer;
		} else if (Boolean.class.equals(type)) {
			t = FieldType.booleanx;
		} else if (Character.class.equals(type)) {
			t = FieldType.string;
		}else if(type.equals(java.sql.Date.class)){
			t = FieldType.date;
		}else if(type.equals(java.util.Date.class)){
			t = FieldType.date;
		}else if(type.equals(Timestamp.class)){
			t = FieldType.timestamp;
		}else if(type.equals(Time.class)){
			t = FieldType.time;
		}else {
			t = FieldType.string;
		}
		return t;
	}
	
	
	
	/**
	 * 是否为集合类型
	 * @author Defender
	 * 2014年2月15日 下午1:41:51
	 * @param field
	 * @return
	 */
    private boolean isCollection(java.lang.reflect.Field field){
    	boolean flag = false;
    	String typeStr = field.getGenericType().toString();
    	if (("interface java.util.Collection".equals(typeStr))
				|| ("interface java.util.HashMap".equals(typeStr))
				|| ("interface java.util.List".equals(typeStr))
				|| ("interface java.util.Map".equals(typeStr))
				|| ("interface java.util.Set".equals(typeStr))
				|| ("class java.util.Collection".equals(typeStr))
				|| ("class java.util.HashMap".equals(typeStr))
				|| ("class java.util.List".equals(typeStr))
				|| ("class java.util.Map".equals(typeStr)) 
				|| ("class java.util.Set".equals(typeStr))) {
    		flag = true;
    	}
    	return flag;
    }
    
	/**
	 * 判断是否为java本身的类型
	 * 
	 * @param clz
	 * @return
	 */
	private boolean isJavaClass(Class<?> clz) {
		return clz != null && clz.getClassLoader() == null;
	}
	
	
	/**
	 * 类型type是否包含在excludeType类型中
	 * 
	 * @param type
	 * @param excludeType
	 * @return
	 */
	private boolean isInclude(Class<?> type, final Class<?>... excludeType) {
		boolean include = false;
		if (excludeType != null) {
			for (Class<?> class1 : excludeType) {
				if (class1.equals(type)) {
					include = true;
					break;
				}
				if (type.isAssignableFrom(class1)) {
					include = true;
					break;
				}
			}
		}
		return include;
	}
	
	/**
	 * 反射对象，获取对象的属性
	 * @param obj
	 * @param excludeType
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public List<Column> toColumns(Object obj, final Class<?>... excludeType)
			throws SecurityException, NoSuchMethodException, Exception {
		Class<?> superClazz = obj.getClass();
		boolean hasAnntation = superClazz.isAnnotationPresent(IndexTable.class);
		List<Column> list = null;
		if(hasAnntation){
			list = this.toColumnsAnnotation(obj, excludeType);
		}else{
			list = infocache.get(superClazz);
			if (list == null) {
				list = this.toColumns(superClazz, excludeType);
			} 
			List<Column> clist = new ArrayList<Column>(list.size());
			for (Column column : list) {
				 clist.add(setColumnValue(obj,column));
			}
			return clist;
		}
		return list;
	}
	
	
	/**
	 * 获取实体类型的属性
	 * @author 李昇
	 * 2014年4月19日 下午1:52:57
	 * @param obj
	 * @param excludeType
	 * @return
	 * @throws Exception
	 */
	private List<Column> toColumnsAnnotation(Object obj, final Class<?>... excludeType) throws Exception{
		List<Column> list = infocache.get(obj.getClass());
		if (list == null) {
			list = this.toColumnsAnnotation(obj.getClass(), excludeType);
		} 
		List<Column> clist = new ArrayList<Column>(list.size());
		for (Column column : list) {
			 clist.add(this.setColumnValue(obj,column));
		}
		return clist;
	}
	
	/**
	 * 将obj某个属性的值赋值给Column
	 * @author 李昇
	 * 2014年4月19日 下午2:03:57
	 * @param obj
	 * @param c
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	private Column setColumnValue(final Object obj,final Column c) throws SecurityException, NoSuchMethodException, Exception{
		if(c==null){
			throw new IllegalArgumentException("列不能为空！");
		}
		Column dest = (Column) c.clone();
		//dest = c;
		Object value = null;
		value = this.getValue(obj, dest.getName());
		if(dest.isForeignkey() && value!=null){
//			IndexField annotation = this.getField(obj, dest.getName()).getAnnotation(IndexField.class);
//			if(annotation!=null){
				value = this.getValue(value, c.getForeignkeyField().getName());
//			}else{
//				
//			}
		}
		dest.setValue(value);
		return dest;
	}
	
	private Field getField(Object obj,String fieldName) throws NoSuchFieldException, SecurityException{
		return obj.getClass().getDeclaredField(fieldName);
	}
	
	/**
	 * 获取字段格式化的值
	 * @author 李昇
	 * 2014年4月19日 下午1:56:26
	 * @param obj
	 * @param fieldName
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public Object getValue(final Object obj, final String fieldName)
			throws SecurityException, NoSuchMethodException, Exception {
		return this.getValue(obj, obj.getClass().getDeclaredField(fieldName).getType(), fieldName);
	}
	
	/**
	 * 获取字段格式化的值
	 * @param obj
	 * @param field
	 * @param excludeType
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public Object getValue(final Object obj, final Class<?> fieldType, final String fieldName)
			throws SecurityException, NoSuchMethodException, Exception {

		Object value = null;
		Class<?> type = fieldType;
		if(TypeUtil.isPrimitive(type)){
			value = this.getBaseTypeValue(obj,fieldType, fieldName);
		}else if (String.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (String) m.invoke(obj);
		} else if(TypeUtil.isDate(type)){
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value =  m.invoke(obj);
			if(this.isDateToLong()){
				if(value==null){
					value = 0L;
				}else{
					value = ((Date)value).getTime();			
				}
			}
		}else {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value =  m.invoke(obj);
		}
		return value;
	}
	
	
	/**
	 * 获取基本类型的值
	 * @param obj
	 * @param field
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws Exception
	 */
	public Object getBaseTypeValue(final Object obj, final Class<?> fieldType, final String fieldName)
			throws SecurityException, NoSuchMethodException, Exception {
		Object value = null;
		Class<?> type = fieldType;
		if (type.isPrimitive()) {// 判断是否为基本类型
			if (double.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Double) m.invoke(obj);
			} else if (float.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Float) m.invoke(obj);
			} else if (long.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Long) m.invoke(obj);
			} else if (int.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Integer) m.invoke(obj);
			} else if (short.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Short) m.invoke(obj);
			} else if (byte.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Byte) m.invoke(obj);
			} else if (boolean.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						IS + NameUtil.firstCharToUpper(fieldName));
				value = (Boolean) m.invoke(obj);
			} else if (char.class.equals(type)) {
				Method m = (Method) obj.getClass().getMethod(
						GET + NameUtil.firstCharToUpper(fieldName));
				value = (Character) m.invoke(obj);
			}
		} else if (Double.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Double) m.invoke(obj);
		} else if (Float.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Float) m.invoke(obj);
		} else if (Long.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Long) m.invoke(obj);
		} else if (Integer.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Integer) m.invoke(obj);
		} else if (Short.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Short) m.invoke(obj);
		} else if (Byte.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Byte) m.invoke(obj);
		} else if (Boolean.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Boolean) m.invoke(obj);
		} else if (Character.class.equals(type)) {
			Method m = (Method) obj.getClass().getMethod(
					GET + NameUtil.firstCharToUpper(fieldName));
			value = (Character) m.invoke(obj);// 调用getter方法获取属性值
		}
		return value;
	}



	/**
	 * 获取实体类型的属性(无属性值)
	 * @author 李昇
	 * 2014年4月19日 下午1:52:52
	 * @param objClass
	 * @param excludeType
	 * @return
	 * @throws Exception
	 */
	private List<Column> toColumnsAnnotation(Class<?> objClass, final Class<?>... excludeType) throws Exception{
		Class<?> superClazz = objClass;
		List<AccessibleObject> members = new ArrayList<AccessibleObject>();
		while (superClazz != null && superClazz != Object.class) {
			members.addAll(Arrays.asList(superClazz.getDeclaredFields()));
			//members.addAll(Arrays.asList(superClazz.getDeclaredMethods()));
			superClazz = superClazz.getSuperclass();
		}
		List<Column> list = infocache.get(objClass);
		if (list == null) {
			list = new ArrayList<Column>();
			String tableName = NameUtil.firstCharToLower(objClass.getSimpleName());
			boolean include = false;
			Table table = new Table("",tableName);
			IndexTable indexTable = objClass.getAnnotation(IndexTable.class);
			if(indexTable!=null){
				if(indexTable.name().equals(IndexTable.DEFAULT_name)){
					table.setAlias(tableName);
				}else{
					table.setAlias(indexTable.name());
				}
			}
			for (AccessibleObject member : members) {
				if (member.isAnnotationPresent(IndexField.class)) {
					member.setAccessible(true);
					if (member instanceof java.lang.reflect.Field) {
						Field field = (java.lang.reflect.Field) member;
						include = this.isInclude(field.getType(), excludeType);
						if (!include) {
							IndexField annotation = member.getAnnotation(IndexField.class);
							Column c = new Column(field.getName());
							this.setProptery(c, field, annotation);
							table.addField(c);
							c.check(this.indexNameHasSalt);
							list.add(c);
						}
					}
				}
			}
			synchronized (infocache) {
				if(list!=null){
					infocache.put(objClass, list);
				}
			}
		}
		return list;
	}
	
	private void setProptery(Column c,Field field,IndexField annotation){
		c.setName(field.getName());
		if(annotation!=null){
			if (annotation.name().equals(IndexField.DEFAULT_name)) {
				c.setAlias(field.getName());
			}else{
				c.setAlias(annotation.name());
			}
			c.setIndex(annotation.index());
			c.setStore(annotation.store());
			c.setForeignkey(annotation.isFK());
			c.setPrimaryKey(annotation.isPK());
			if(annotation.type()==IndexField.DEFAULT_type){
				c.setFieldType(getFieldType(field.getType()));
			}else{
				c.setFieldType(annotation.type());
			}
			c.setForeignkeyField(new Column(annotation.fk()));
		}
	}
	

	/**
	 * 获取字段的值
	 * @author 李昇
	 * 2014年4月19日 下午1:54:02
	 * @param owner
	 * @param fieldName
	 * @return
	 */
	public Object getFieldValue(Object owner, String fieldName) {
		Object value=null;
		Object o = this.invokeMethod(owner, fieldName, null);
		value = o;
		return value;
	}
	
	
	/**
	 * 执行某个Field的getField方法
	 * @param owner 类
	 * @param fieldName 类的属性名称
	 * @param args 参数，默认为null
	 * @return
	 */
	public Object invokeMethod(Object owner, String fieldName, Object[] args) {
		Class<? extends Object> ownerClass = owner.getClass();
		Object o = null;
		// fieldName -> FieldName
		Method method = null;
		try {
			String methodName = NameUtil.firstCharToUpper(fieldName);
			method = ownerClass.getMethod(GET + methodName);
		} catch (SecurityException e) {
			 e.printStackTrace();
		} catch (NoSuchMethodException e) {
			 e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		// invoke getMethod
		try {
			o=method.invoke(owner);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return o;
	}
	
	
	
}
